﻿#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using PHMEE.CommonUtils;
using PHMEE.Localization;
using Volo.Abp;

namespace PHMEE.EnergyEfficiency;

public class FuelAppService : PHMEEAppService, IFuelAppService
{
    private readonly IFlowMeterTdRepository _flowMeterTdRepository;
    private readonly FuelConsumptionCalculator _fuelConsumptionCalculator;
    private readonly IPHMEETimeProvider _phmeeTimeProvider;
    private readonly IBunkerReportRepository _bunkerReportRepository;
    private readonly VesselEeConfigDomainService _vesselEeConfigDomainService;
    private readonly ILogAbstractRepository _logAbstractRepository;
    private readonly IVesselEeConfigRepository _vesselEeConfigRepository;

    public FuelAppService(IFlowMeterTdRepository flowMeterTdRepository,
        FuelConsumptionCalculator fuelConsumptionCalculator,
        IPHMEETimeProvider phmeeTimeProvider,
        IBunkerReportRepository bunkerReportRepository,
        VesselEeConfigDomainService vesselEeConfigDomainService,
        ILogAbstractRepository logAbstractRepository,
        IVesselEeConfigRepository vesselEeConfigRepository)
    {
        LocalizationResource = typeof(EnergyEfficiencyResource);
        
        _flowMeterTdRepository = flowMeterTdRepository;
        _fuelConsumptionCalculator = fuelConsumptionCalculator;
        _phmeeTimeProvider = phmeeTimeProvider;
        _bunkerReportRepository = bunkerReportRepository;
        _vesselEeConfigDomainService = vesselEeConfigDomainService;
        _logAbstractRepository = logAbstractRepository;
        _vesselEeConfigRepository = vesselEeConfigRepository;
    }

    // /// <inheritdoc />
    // [Obsolete]
    // public async Task<CurrentFuelTypeDto> CurrentFuelTypeAsync(Guid vesselId)
    // {
    //     var now = _phmeeTimeProvider.GetUtcNow();
    //
    //     var meLatestFuelType = await _flowMeterTdRepository.GetMeLatestFuelTypeAsync(vesselId, now);
    //     var aeLatestFuelType = await _flowMeterTdRepository.GetAeLatestFuelTypeAsync(vesselId, now);
    //     var boilerLatestFuelType = await _flowMeterTdRepository.GetBoLatestFuelTypeAsync(vesselId, now);
    //
    //     return new CurrentFuelTypeDto(meLatestFuelType, aeLatestFuelType, boilerLatestFuelType);
    // }

    // /// <inheritdoc />
    // [Obsolete]
    // public async Task<IReadOnlyList<FuelMeasurement>> FuelConsumptionForHour(Guid vesselId,
    //     DateTime? hour)
    // {
    //     var utcNow = _phmeeTimeProvider.GetUtcNow();
    //
    //     // 只有采集版有小时油耗量，不需要实现基于 LogAbstract 的查询
    //     // 查询小时油耗时，选择的小时必须小于当前小时
    //     var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForHourQuery(hour, utcNow);
    //
    //     var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
    //     if (vesselEeConfig is null)
    //     {
    //         return new List<FuelMeasurement>();
    //     }
    //
    //     var fuelTypes = vesselEeConfig.FuelTypes;
    //
    //     var fuelMeasurements = new List<FuelMeasurement>();
    //
    //     Dictionary<FuelType, float> fuelConsumptions =
    //         await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodAsync(vesselId, startDateTime,
    //             endDateTime);
    //
    //     foreach (FuelType fuelType in fuelTypes)
    //     {
    //         if (fuelConsumptions.TryGetValue(fuelType, out var consumption))
    //         {
    //             fuelMeasurements.Add(new FuelMeasurement(fuelType, consumption));
    //         }
    //         else
    //         {
    //             fuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
    //         }
    //     }
    //
    //     return fuelMeasurements;
    // }

    /// <inheritdoc />
    public async Task<FuelConsumptionDto> FuelConsumptionForDay(Guid vesselId,
        DateTime? date)
    {
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new FuelConsumptionDto();
        }

        if (vesselEeConfig.IsFuelAutoCollect)
        {
            return await FuelConsumptionForDayByFlowMeter(vesselId, date, vesselEeConfig);
        }
        else
        {
            return await FuelConsumptionForDayByLA(vesselId, date, vesselEeConfig);
        }
    }

    private async Task<FuelConsumptionDto> FuelConsumptionForDayByFlowMeter(Guid vesselId,
        DateTime? date, VesselEeConfig vesselEeConfig)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForDayQuery(date, utcNow);

        var fuelTypes = vesselEeConfig.FuelTypes;

        var (meConsumptions, aeConsumptions, boilerConsumptions, totalConsumptions) =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodGroupedByUnitAsync(vesselId,
                startDateTime, endDateTime);

        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();
        var totalFuelMeasurements = new List<FuelMeasurement>();

        foreach (FuelType fuelType in fuelTypes)
        {
            meFuelMeasurements.Add(meConsumptions.TryGetValue(fuelType, out var meConsumption)
                ? new FuelMeasurement(fuelType, meConsumption)
                : new FuelMeasurement(fuelType, 0));

            aeFuelMeasurements.Add(aeConsumptions.TryGetValue(fuelType, out var aeConsumption)
                ? new FuelMeasurement(fuelType, aeConsumption)
                : new FuelMeasurement(fuelType, 0));

            boilerFuelMeasurements.Add(boilerConsumptions.TryGetValue(fuelType, out var boConsumption)
                ? new FuelMeasurement(fuelType, boConsumption)
                : new FuelMeasurement(fuelType, 0));

            totalFuelMeasurements.Add(totalConsumptions.TryGetValue(fuelType, out var totalConsumption)
                ? new FuelMeasurement(fuelType, totalConsumption)
                : new FuelMeasurement(fuelType, 0));
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = totalFuelMeasurements
        };
    }

    private async Task<FuelConsumptionDto> FuelConsumptionForDayByLA(Guid vesselId,
        DateTime? date, VesselEeConfig vesselEeConfig)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForDayQuery(date, utcNow);

        var fuelTypes = vesselEeConfig.FuelTypes;
        
        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();
        var totalFuelMeasurements = new List<FuelMeasurement>();

        foreach (FuelType fuelType in fuelTypes)
        {
            float meConsumption = logAbstracts.GetMEConsumptionByFuelType(fuelType);
            meFuelMeasurements.Add(new FuelMeasurement(fuelType, meConsumption));

            float aeConsumption = logAbstracts.GetAEConsumptionByFuelType(fuelType);
            aeFuelMeasurements.Add(new FuelMeasurement(fuelType, aeConsumption));

            float boConsumption = logAbstracts.GetBoilerConsumptionByFuelType(fuelType);
            boilerFuelMeasurements.Add(new FuelMeasurement(fuelType, boConsumption));

            float totalConsumption = meConsumption + aeConsumption + boConsumption;
            totalFuelMeasurements.Add(new FuelMeasurement(fuelType, totalConsumption));
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = totalFuelMeasurements
        };
    }

    /// <inheritdoc />    
    public async Task<FuelConsumptionDto> FuelConsumptionForDateTimePeriod(Guid vesselId,
        DateTime startDateTime, DateTime endDateTime)
    {
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new FuelConsumptionDto();
        }

        if (vesselEeConfig.IsFuelAutoCollect)
        {
            return await FuelConsumptionForDateTimePeriodByFlowMeter(vesselId, startDateTime, endDateTime, vesselEeConfig);
        }
        else
        {
            return await FuelConsumptionForDateTimePeriodByLA(vesselId, startDateTime, endDateTime, vesselEeConfig);
        }
    }

    private async Task<FuelConsumptionDto> FuelConsumptionForDateTimePeriodByFlowMeter(Guid vesselId,
        DateTime startDateTime, DateTime endDateTime, VesselEeConfig vesselEeConfig)
    {
        // 将 DateTime 的 Kind 设置为 Utc
        startDateTime = startDateTime.ToUtcKind();
        endDateTime = endDateTime.ToUtcKind();

        var fuelTypes = vesselEeConfig.FuelTypes;

        var (meConsumptions, aeConsumptions, boilerConsumptions, totalConsumptions) =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodGroupedByUnitAsync(vesselId,
                startDateTime, endDateTime);

        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();
        var totalFuelMeasurements = new List<FuelMeasurement>();

        foreach (FuelType fuelType in fuelTypes)
        {
            meFuelMeasurements.Add(meConsumptions.TryGetValue(fuelType, out var meConsumption)
                ? new FuelMeasurement(fuelType, meConsumption)
                : new FuelMeasurement(fuelType, 0));

            aeFuelMeasurements.Add(aeConsumptions.TryGetValue(fuelType, out var aeConsumption)
                ? new FuelMeasurement(fuelType, aeConsumption)
                : new FuelMeasurement(fuelType, 0));

            boilerFuelMeasurements.Add(boilerConsumptions.TryGetValue(fuelType, out var boConsumption)
                ? new FuelMeasurement(fuelType, boConsumption)
                : new FuelMeasurement(fuelType, 0));

            totalFuelMeasurements.Add(totalConsumptions.TryGetValue(fuelType, out var totalConsumption)
                ? new FuelMeasurement(fuelType, totalConsumption)
                : new FuelMeasurement(fuelType, 0));
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = totalFuelMeasurements
        };
    }

    private async Task<FuelConsumptionDto> FuelConsumptionForDateTimePeriodByLA(Guid vesselId,
        DateTime startDateTime, DateTime endDateTime, VesselEeConfig vesselEeConfig)
    {
        // 将 DateTime 的 Kind 设置为 Utc
        startDateTime = startDateTime.ToUtcKind();
        endDateTime = endDateTime.ToUtcKind();

        var fuelTypes = vesselEeConfig.FuelTypes;

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();
        var totalFuelMeasurements = new List<FuelMeasurement>();

        foreach (FuelType fuelType in fuelTypes)
        {
            float meConsumption = logAbstracts.GetMEConsumptionByFuelType(fuelType);
            meFuelMeasurements.Add(new FuelMeasurement(fuelType, meConsumption));

            float aeConsumption = logAbstracts.GetAEConsumptionByFuelType(fuelType);
            aeFuelMeasurements.Add(new FuelMeasurement(fuelType, aeConsumption));

            float boConsumption = logAbstracts.GetBoilerConsumptionByFuelType(fuelType);
            boilerFuelMeasurements.Add(new FuelMeasurement(fuelType, boConsumption));
            
            float totalConsumption = meConsumption + aeConsumption + boConsumption;
            totalFuelMeasurements.Add(new FuelMeasurement(fuelType, totalConsumption));
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = totalFuelMeasurements
        };
    }

    /// <inheritdoc />
    public async Task<FuelConsumptionDto> FuelConsumptionForVoyage(Guid vesselId, DateTime voyageStartTime,
        DateTime voyageEndTime)
    {
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new FuelConsumptionDto();
        }

        if (vesselEeConfig.IsFuelAutoCollect)
        {
            return await FuelConsumptionForVoyageByFlowMeter(vesselId, voyageStartTime, voyageEndTime, vesselEeConfig);
        }
        else
        {
            return await FuelConsumptionForVoyageByLA(vesselId, voyageStartTime, voyageEndTime, vesselEeConfig);
        }
    }

    private async Task<FuelConsumptionDto> FuelConsumptionForVoyageByFlowMeter(Guid vesselId, 
        DateTime voyageStartTime, DateTime voyageEndTime, VesselEeConfig vesselEeConfig)
    {
        // 将 DateTime 的 Kind 设置为 Utc
        voyageStartTime = voyageStartTime.ToUtcKind();
        voyageEndTime = voyageEndTime.ToUtcKind();

        var fuelTypes = vesselEeConfig.FuelTypes;

        var (meConsumptions, aeConsumptions, boilerConsumptions, totalConsumptions) =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodGroupedByUnitAsync(vesselId,
                voyageStartTime, voyageEndTime);

        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();
        var totalFuelMeasurements = new List<FuelMeasurement>();

        foreach (FuelType fuelType in fuelTypes)
        {
            meFuelMeasurements.Add(meConsumptions.TryGetValue(fuelType, out var meConsumption)
                ? new FuelMeasurement(fuelType, meConsumption)
                : new FuelMeasurement(fuelType, 0));

            aeFuelMeasurements.Add(aeConsumptions.TryGetValue(fuelType, out var aeConsumption)
                ? new FuelMeasurement(fuelType, aeConsumption)
                : new FuelMeasurement(fuelType, 0));

            boilerFuelMeasurements.Add(boilerConsumptions.TryGetValue(fuelType, out var boConsumption)
                ? new FuelMeasurement(fuelType, boConsumption)
                : new FuelMeasurement(fuelType, 0));

            totalFuelMeasurements.Add(totalConsumptions.TryGetValue(fuelType, out var totalConsumption)
                ? new FuelMeasurement(fuelType, totalConsumption)
                : new FuelMeasurement(fuelType, 0));
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = totalFuelMeasurements
        };
    }

    private async Task<FuelConsumptionDto> FuelConsumptionForVoyageByLA(Guid vesselId,
        DateTime voyageStartTime, DateTime voyageEndTime, VesselEeConfig vesselEeConfig)
    {
        // 将 DateTime 的 Kind 设置为 Utc
        voyageStartTime = voyageStartTime.ToUtcKind();
        voyageEndTime = voyageEndTime.ToUtcKind();

        var fuelTypes = vesselEeConfig.FuelTypes;

        var voyageLogAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, voyageStartTime, voyageEndTime);

        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();
        var totalFuelMeasurements = new List<FuelMeasurement>();

        foreach (FuelType fuelType in fuelTypes)
        {
            float meConsumption = voyageLogAbstracts.GetMEConsumptionByFuelType(fuelType);
            meFuelMeasurements.Add(new FuelMeasurement(fuelType, meConsumption));

            float aeConsumption = voyageLogAbstracts.GetAEConsumptionByFuelType(fuelType);
            aeFuelMeasurements.Add(new FuelMeasurement(fuelType, aeConsumption));

            float boConsumption = voyageLogAbstracts.GetBoilerConsumptionByFuelType(fuelType);
            boilerFuelMeasurements.Add(new FuelMeasurement(fuelType, boConsumption));

            float totalConsumption = meConsumption + aeConsumption + boConsumption;
            totalFuelMeasurements.Add(new FuelMeasurement(fuelType, totalConsumption));
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = totalFuelMeasurements
        };
    }
    
    /// <inheritdoc />
    public async Task<FuelConsumptionDto> FuelConsumptionForYear(Guid vesselId,
        int? year)
    {
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new FuelConsumptionDto();
        }

        if (vesselEeConfig.IsFuelAutoCollect)
        {
            return await FuelConsumptionForYearByFlowMeter(vesselId, year, vesselEeConfig);
        }
        else
        {
            return await FuelConsumptionForYearByLA(vesselId, year, vesselEeConfig);
        }
    }
    
    private async Task<FuelConsumptionDto> FuelConsumptionForYearByFlowMeter(Guid vesselId,
        int? year, VesselEeConfig vesselEeConfig)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var (_, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        var fuelTypes = vesselEeConfig.FuelTypes;

        var (meConsumptions, aeConsumptions, boilerConsumptions, totalConsumptions) =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodGroupedByUnitAsync(vesselId,
                startDateTime, endDateTime);

        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();
        var totalFuelMeasurements = new List<FuelMeasurement>();

        foreach (FuelType fuelType in fuelTypes)
        {
            meFuelMeasurements.Add(meConsumptions.TryGetValue(fuelType, out var meConsumption)
                ? new FuelMeasurement(fuelType, meConsumption)
                : new FuelMeasurement(fuelType, 0));

            aeFuelMeasurements.Add(aeConsumptions.TryGetValue(fuelType, out var aeConsumption)
                ? new FuelMeasurement(fuelType, aeConsumption)
                : new FuelMeasurement(fuelType, 0));

            boilerFuelMeasurements.Add(boilerConsumptions.TryGetValue(fuelType, out var boConsumption)
                ? new FuelMeasurement(fuelType, boConsumption)
                : new FuelMeasurement(fuelType, 0));

            totalFuelMeasurements.Add(totalConsumptions.TryGetValue(fuelType, out var totalConsumption)
                ? new FuelMeasurement(fuelType, totalConsumption)
                : new FuelMeasurement(fuelType, 0));
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = totalFuelMeasurements
        };
    }
    
    private async Task<FuelConsumptionDto> FuelConsumptionForYearByLA(Guid vesselId,
        int? year, VesselEeConfig vesselEeConfig)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var (_, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        var fuelTypes = vesselEeConfig.FuelTypes;

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();
        var totalFuelMeasurements = new List<FuelMeasurement>();

        foreach (FuelType fuelType in fuelTypes)
        {
            float meConsumption = logAbstracts.GetMEConsumptionByFuelType(fuelType);
            meFuelMeasurements.Add(new FuelMeasurement(fuelType, meConsumption));

            float aeConsumption = logAbstracts.GetAEConsumptionByFuelType(fuelType);
            aeFuelMeasurements.Add(new FuelMeasurement(fuelType, aeConsumption));

            float boConsumption = logAbstracts.GetBoilerConsumptionByFuelType(fuelType);
            boilerFuelMeasurements.Add(new FuelMeasurement(fuelType, boConsumption));

            float totalConsumption = meConsumption + aeConsumption + boConsumption;
            totalFuelMeasurements.Add(new FuelMeasurement(fuelType, totalConsumption));
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = totalFuelMeasurements
        };
    }

    #region 应急油耗量查询

    /// <inheritdoc />
    public async Task<FuelConsumptionDto> EmrgFuelConsumptionForDay(Guid vesselId, DateTime? date)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForDayQuery(date, utcNow);

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new FuelConsumptionDto();
        }

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        // 目前应急油耗只有副机，但为了返回格式统一，所以主机和锅炉也返回，只是值为 0
        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            meFuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
            aeFuelMeasurements.Add(new FuelMeasurement(fuelType,
                logAbstracts.GetAEEmrgTotalConsumptionByFuelType(fuelType)));
            boilerFuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
        }

        // 由于只有副机机组有应急油耗，所以副机机组的应急油耗和总油耗是一样的
        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = aeFuelMeasurements
        };
    }

    /// <inheritdoc />
    public async Task<FuelConsumptionDto> EmrgFuelConsumptionForDateTimePeriod(Guid vesselId, DateTime startDateTime,
        DateTime endDateTime)
    {
        // 将 DateTime 的 Kind 设置为 Utc
        startDateTime = startDateTime.ToUtcKind();
        endDateTime = endDateTime.ToUtcKind();

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new FuelConsumptionDto();
        }

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        // 目前应急油耗只有副机，但为了返回格式统一，所以主机和锅炉也返回，只是值为 0
        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            meFuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
            aeFuelMeasurements.Add(new FuelMeasurement(fuelType,
                logAbstracts.GetAEEmrgTotalConsumptionByFuelType(fuelType)));
            boilerFuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
        }

        // 由于只有副机机组有应急油耗，所以副机机组的应急油耗和总油耗是一样的
        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = aeFuelMeasurements
        };
    }

    /// <inheritdoc />
    public async Task<FuelConsumptionDto> EmrgFuelConsumptionForVoyage(Guid vesselId, DateTime voyageStartTime,
        DateTime voyageEndTime)
    {
        // 将 DateTime 的 Kind 设置为 Utc
        voyageStartTime = voyageStartTime.ToUtcKind();
        voyageEndTime = voyageEndTime.ToUtcKind();

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new FuelConsumptionDto();
        }

        var voyageLogAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, voyageStartTime, voyageEndTime);

        // 目前应急油耗只有副机，但为了返回格式统一，所以主机和锅炉也返回，只是值为 0
        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            meFuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
            aeFuelMeasurements.Add(new FuelMeasurement(fuelType,
                voyageLogAbstracts.GetAEEmrgTotalConsumptionByFuelType(fuelType)));
            boilerFuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
        }

        // 由于只有副机机组有应急油耗，所以副机机组的应急油耗和总油耗是一样的
        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = aeFuelMeasurements
        };
    }

    /// <inheritdoc />
    public async Task<FuelConsumptionDto> EmrgFuelConsumptionForYear(Guid vesselId, int? year)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var (_, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new FuelConsumptionDto();
        }

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        // 目前应急油耗只有副机，但为了返回格式统一，所以主机和锅炉也返回，只是值为 0
        var meFuelMeasurements = new List<FuelMeasurement>();
        var aeFuelMeasurements = new List<FuelMeasurement>();
        var boilerFuelMeasurements = new List<FuelMeasurement>();

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            meFuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
            aeFuelMeasurements.Add(new FuelMeasurement(fuelType,
                logAbstracts.GetAEEmrgTotalConsumptionByFuelType(fuelType)));
            boilerFuelMeasurements.Add(new FuelMeasurement(fuelType, 0));
        }

        // 由于只有副机机组有应急油耗，所以副机机组的应急油耗和总油耗是一样的
        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelMeasurements,
            AEFuelConsumptions = aeFuelMeasurements,
            BoilerFuelConsumptions = boilerFuelMeasurements,
            FuelConsumptions = aeFuelMeasurements
        };
    }

    #endregion 应急油耗量查询

    #region 混合油耗查询

    // 手填版没有混合油耗的说法，默认的接口就统计了所有的油耗，包含应急油耗

    public async Task<FuelConsumptionDto> MixFuelConsumptionForDay(Guid vesselId, DateTime? date)
    {
        var flowMeterFuelConsumptionDto = await FuelConsumptionForDay(vesselId, date);
        var emrgFuelConsumptionDto = await EmrgFuelConsumptionForDay(vesselId, date);
        var result = MergeFuelConsumptionDtos(new[] { flowMeterFuelConsumptionDto, emrgFuelConsumptionDto });
        return result;
    }

    public async Task<FuelConsumptionDto> MixFuelConsumptionForDateTimePeriod(Guid vesselId, DateTime startDateTime,
        DateTime endDateTime)
    {
        var flowMeterFuelConsumptionDto = await FuelConsumptionForDateTimePeriod(vesselId, startDateTime, endDateTime);
        var emrgFuelConsumptionDto = await EmrgFuelConsumptionForDateTimePeriod(vesselId, startDateTime, endDateTime);
        var result = MergeFuelConsumptionDtos(new[] { flowMeterFuelConsumptionDto, emrgFuelConsumptionDto });
        return result;
    }

    public async Task<FuelConsumptionDto> MixFuelConsumptionForVoyage(Guid vesselId, DateTime voyageStartTime,
        DateTime voyageEndTime)
    {
        var flowMeterFuelConsumptionDto = await FuelConsumptionForVoyage(vesselId, voyageStartTime, voyageEndTime);
        var emrgFuelConsumptionDto = await EmrgFuelConsumptionForVoyage(vesselId, voyageStartTime, voyageEndTime);
        var result = MergeFuelConsumptionDtos(new[] { flowMeterFuelConsumptionDto, emrgFuelConsumptionDto });
        return result;
    }

    public async Task<FuelConsumptionDto> MixFuelConsumptionForYear(Guid vesselId, int? year)
    {
        var flowMeterFuelConsumptionDto = await FuelConsumptionForYear(vesselId, year);
        var emrgFuelConsumptionDto = await EmrgFuelConsumptionForYear(vesselId, year);
        var result = MergeFuelConsumptionDtos(new[] { flowMeterFuelConsumptionDto, emrgFuelConsumptionDto });
        return result;
    }

    private FuelConsumptionDto MergeFuelConsumptionDtos(IEnumerable<FuelConsumptionDto> dtos)
    {
        var meFuelConsumptions = new List<FuelMeasurement>();
        var aeFuelConsumptions = new List<FuelMeasurement>();
        var boilerFuelConsumptions = new List<FuelMeasurement>();
        var totalFuelConsumptions = new List<FuelMeasurement>();

        foreach (var dto in dtos)
        {
            meFuelConsumptions = MergeFuelMeasurements(dto.MEFuelConsumptions, meFuelConsumptions);
            aeFuelConsumptions = MergeFuelMeasurements(dto.AEFuelConsumptions, aeFuelConsumptions);
            boilerFuelConsumptions = MergeFuelMeasurements(dto.BoilerFuelConsumptions, boilerFuelConsumptions);
            totalFuelConsumptions = MergeFuelMeasurements(dto.FuelConsumptions, totalFuelConsumptions);
        }

        return new FuelConsumptionDto
        {
            MEFuelConsumptions = meFuelConsumptions,
            AEFuelConsumptions = aeFuelConsumptions,
            BoilerFuelConsumptions = boilerFuelConsumptions,
            FuelConsumptions = totalFuelConsumptions
        };
    }

    private List<FuelMeasurement> MergeFuelMeasurements(List<FuelMeasurement> fuelMeasurements1,
        List<FuelMeasurement> fuelMeasurements2)
    {
        var result = new List<FuelMeasurement>();
        foreach (var fuelMeasurement1 in fuelMeasurements1)
        {
            var fuelMeasurement2 = fuelMeasurements2.FirstOrDefault(x => x.FuelType == fuelMeasurement1.FuelType);
            if (fuelMeasurement2 is null)
            {
                result.Add(fuelMeasurement1);
            }
            else
            {
                result.Add(new FuelMeasurement(fuelMeasurement1.FuelType,
                    fuelMeasurement1.Value + fuelMeasurement2.Value));
            }
        }

        return result;
    }

    #endregion 混合油耗查询

    /// <inheritdoc />
    public async Task<IReadOnlyList<FuelMeasurement>> FuelInventoryAsync(Guid vesselId, DateTime? time)
    {
        // 默认取当前时间
        var selectedTime = time ?? _phmeeTimeProvider.GetUtcNow();

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new List<FuelMeasurement>();
        }

        // 存油量手填
        var latestLogAbstract =
            await _logAbstractRepository.GetLatestLogAbstractOrDefaultAsync(vesselId, selectedTime);

        var result = new List<FuelMeasurement>(vesselEeConfig.FuelTypes.Count);
        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            var robMass = latestLogAbstract?.GetROBByFuelType(fuelType);
            var fuelMeasurement = robMass is null
                ? new FuelMeasurement(fuelType, 0)
                : new FuelMeasurement(fuelType, robMass.Value);
            result.Add(fuelMeasurement);
        }

        return result;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<FuelMeasurement>> FuelInventoryFromLogAbstractAsync(Guid vesselId, DateTime? time)
    {
        // 默认取当前时间
        var selectedTime = time ?? _phmeeTimeProvider.GetUtcNow();

        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new List<FuelMeasurement>();
        }

        // 存油量手填
        var latestLogAbstract =
            await _logAbstractRepository.GetLatestLogAbstractOrDefaultAsync(vesselId, selectedTime);

        var result = new List<FuelMeasurement>(vesselEeConfig.FuelTypes.Count);
        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            var robMass = latestLogAbstract?.GetROBByFuelType(fuelType);
            var fuelMeasurement = robMass is null
                ? new FuelMeasurement(fuelType, 0)
                : new FuelMeasurement(fuelType, robMass.Value);
            result.Add(fuelMeasurement);
        }

        return result;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<FuelConsumptionComparisonDto>> AverageFuelConsumptionComparison(Guid vesselId,
        DateTime ts1StartDateTime, DateTime ts1EndDateTime,
        DateTime ts2StartDateTime, DateTime ts2EndDateTime)
    {
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        if (vesselEeConfig.IsFuelAutoCollect)
        {
            return await AverageFuelConsumptionComparisonByFlowMeter(vesselId, ts1StartDateTime, ts1EndDateTime,
                ts2StartDateTime, ts2EndDateTime, vesselEeConfig);
        }
        else
        {
            return await AverageFuelConsumptionComparisonByLA(vesselId, ts1StartDateTime, ts1EndDateTime,
                ts2StartDateTime, ts2EndDateTime, vesselEeConfig);
        }
    }
            
    private async Task<IReadOnlyList<FuelConsumptionComparisonDto>> AverageFuelConsumptionComparisonByFlowMeter(Guid vesselId,
        DateTime ts1StartDateTime, DateTime ts1EndDateTime,
        DateTime ts2StartDateTime, DateTime ts2EndDateTime,
        VesselEeConfig vesselEeConfig)
    {
        var ts1Hours = QueryDateTimeUtils.GetHoursBetween(ts1StartDateTime, ts1EndDateTime);
        var ts2Hours = QueryDateTimeUtils.GetHoursBetween(ts2StartDateTime, ts2EndDateTime);
        if (ts1Hours <= 0 || ts2Hours <= 0)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        var fuelConsumptionComparisons = new List<FuelConsumptionComparisonDto>();

        Dictionary<FuelType, float> t1FuelConsumptionsDict =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodAsync(vesselId, ts1StartDateTime,
                ts1EndDateTime);
        Dictionary<FuelType, float> t2FuelConsumptionsDict =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodAsync(vesselId, ts2StartDateTime,
                ts2EndDateTime);

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            // ts1 平均每小时的耗油量
            float ts1Consumption = 0f;
            if (t1FuelConsumptionsDict.TryGetValue(fuelType, out var t1Consumption))
            {
                ts1Consumption = t1Consumption / ts1Hours;
            }

            // ts2 平均每小时的耗油量
            float ts2Consumption = 0f;
            if (t2FuelConsumptionsDict.TryGetValue(fuelType, out var t2Consumption))
            {
                ts2Consumption = t2Consumption / ts2Hours;
            }

            fuelConsumptionComparisons.Add(new FuelConsumptionComparisonDto(fuelType, ts1Consumption, ts2Consumption));
        }

        return fuelConsumptionComparisons;
    }
    
    private async Task<IReadOnlyList<FuelConsumptionComparisonDto>> AverageFuelConsumptionComparisonByLA(Guid vesselId,
        DateTime ts1StartDateTime, DateTime ts1EndDateTime,
        DateTime ts2StartDateTime, DateTime ts2EndDateTime,
        VesselEeConfig vesselEeConfig)
    {
        var ts1Hours = QueryDateTimeUtils.GetHoursBetween(ts1StartDateTime, ts1EndDateTime);
        var ts2Hours = QueryDateTimeUtils.GetHoursBetween(ts2StartDateTime, ts2EndDateTime);
        if (ts1Hours <= 0 || ts2Hours <= 0)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        var fuelConsumptionComparisons = new List<FuelConsumptionComparisonDto>();

        var ts1LogAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, ts1StartDateTime, ts1EndDateTime);
        var ts2LogAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, ts2StartDateTime, ts2EndDateTime);

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            // ts1 平均每小时的耗油量
            float ts1Consumption = ts1LogAbstracts.GetTotalWithEmrgConsumptionByFuelType(fuelType) / ts1Hours;

            // ts2 平均每小时的耗油量
            float ts2Consumption = ts2LogAbstracts.GetTotalWithEmrgConsumptionByFuelType(fuelType) / ts2Hours;

            fuelConsumptionComparisons.Add(new FuelConsumptionComparisonDto(fuelType, ts1Consumption, ts2Consumption));
        }

        return fuelConsumptionComparisons;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<FuelConsumptionComparisonDto>> VoyageAverageFuelConsumptionComparison(Guid vesselId,
        DateTime voyage1StartTime, DateTime voyage1EndTime, DateTime voyage2StartTime, DateTime voyage2EndTime)
    {
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        if (vesselEeConfig.IsFuelAutoCollect)
        {
            return await VoyageAverageFuelConsumptionComparison(vesselId, voyage1StartTime, voyage1EndTime,
                voyage2StartTime, voyage2EndTime, vesselEeConfig);
        }
        else
        {
            return await VoyageAverageFuelConsumptionComparisonByLA(vesselId, voyage1StartTime, voyage1EndTime,
                voyage2StartTime, voyage2EndTime, vesselEeConfig);
        }
    }
        
    private async Task<IReadOnlyList<FuelConsumptionComparisonDto>> VoyageAverageFuelConsumptionComparison(Guid vesselId,
        DateTime voyage1StartTime, DateTime voyage1EndTime, DateTime voyage2StartTime, DateTime voyage2EndTime,
        VesselEeConfig vesselEeConfig)
    {
        var voyage1Hours = QueryDateTimeUtils.GetHoursBetween(voyage1StartTime, voyage1EndTime);
        var voyage2Hours = QueryDateTimeUtils.GetHoursBetween(voyage2StartTime, voyage2EndTime);
        if (voyage1Hours <= 0 || voyage2Hours <= 0)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        var fuelConsumptionComparisons = new List<FuelConsumptionComparisonDto>();

        Dictionary<FuelType, float> v1FuelConsumptionsDict =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodAsync(vesselId, voyage1StartTime,
                voyage1EndTime);
        Dictionary<FuelType, float> v2FuelConsumptionsDict =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodAsync(vesselId, voyage2StartTime,
                voyage2EndTime);

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            // ts1 平均每小时的耗油量
            float voyage1Consumption = 0f;
            if (v1FuelConsumptionsDict.TryGetValue(fuelType, out var v1Consumption))
            {
                voyage1Consumption = v1Consumption / voyage1Hours;
            }

            // ts2 平均每小时的耗油量
            float voyage2Consumption = 0f;
            if (v2FuelConsumptionsDict.TryGetValue(fuelType, out var v2Consumption))
            {
                voyage2Consumption = v2Consumption / voyage2Hours;
            }

            fuelConsumptionComparisons.Add(new FuelConsumptionComparisonDto(fuelType, voyage1Consumption,
                voyage2Consumption));
        }

        return fuelConsumptionComparisons;
    }
    
    private async Task<IReadOnlyList<FuelConsumptionComparisonDto>> VoyageAverageFuelConsumptionComparisonByLA(
        Guid vesselId,
        DateTime voyage1StartTime, DateTime voyage1EndTime, DateTime voyage2StartTime, DateTime voyage2EndTime,
        VesselEeConfig vesselEeConfig)
    {
        var voyage1Hours = QueryDateTimeUtils.GetHoursBetween(voyage1StartTime, voyage1EndTime);
        var voyage2Hours = QueryDateTimeUtils.GetHoursBetween(voyage2StartTime, voyage2EndTime);
        if (voyage1Hours <= 0 || voyage2Hours <= 0)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        var fuelConsumptionComparisons = new List<FuelConsumptionComparisonDto>();

        var voyage1LogAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, voyage1StartTime, voyage1EndTime);
        var voyage2LogAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, voyage2StartTime, voyage2EndTime);

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            // ts1 平均每小时的耗油量
            float voyage1Consumption = voyage1LogAbstracts.GetTotalWithEmrgConsumptionByFuelType(fuelType) / voyage1Hours;

            // ts2 平均每小时的耗油量
            float voyage2Consumption = voyage2LogAbstracts.GetTotalWithEmrgConsumptionByFuelType(fuelType) / voyage2Hours;

            fuelConsumptionComparisons.Add(new FuelConsumptionComparisonDto(fuelType, voyage1Consumption,
                voyage2Consumption));
        }

        return fuelConsumptionComparisons;
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<FuelConsumptionComparisonDto>> YearAverageFuelConsumptionComparison(Guid vesselId,
        int year1, int year2)
    {
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        if (vesselEeConfig.IsFuelAutoCollect)
        {
            return await YearAverageFuelConsumptionComparison(vesselId, year1, year2, vesselEeConfig);
        }
        else
        {
            return await YearAverageFuelConsumptionComparisonByLA(vesselId, year1, year2, vesselEeConfig);
        }
    }
        
    private async Task<IReadOnlyList<FuelConsumptionComparisonDto>> YearAverageFuelConsumptionComparison(
        Guid vesselId, int year1, int year2, VesselEeConfig vesselEeConfig)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        // TODO: 是否允许比较当年的油耗？时间用年的时间还是航行时间？
        var (_, year1StartDate, year1EndDate) = QueryDateTimeUtils.ProcessForPastYearQuery(year1, utcNow);

        var (_, year2StartDate, year2EndDate) = QueryDateTimeUtils.ProcessForPastYearQuery(year2, utcNow);

        // year1 对应的小时数
        float ty1Hours = QueryDateTimeUtils.GetHoursBetween(year1StartDate, year1EndDate);

        // year2 对应的小时数
        float ty2Hours = QueryDateTimeUtils.GetHoursBetween(year2StartDate, year2EndDate);

        if (ty1Hours <= 0 || ty2Hours <= 0)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        var fuelConsumptionComparisons = new List<FuelConsumptionComparisonDto>();

        Dictionary<FuelType, float> ty1FuelConsumptionsDict =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodAsync(vesselId, year1StartDate,
                year1EndDate);
        Dictionary<FuelType, float> ty2FuelConsumptionsDict =
            await _fuelConsumptionCalculator.GetFuelConsumptionForDateTimePeriodAsync(vesselId, year2StartDate,
                year2EndDate);

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            // ts1 平均每小时的耗油量
            float ty1Consumption = 0f;
            if (ty1FuelConsumptionsDict.TryGetValue(fuelType, out var t1Consumption))
            {
                ty1Consumption = t1Consumption / ty1Hours;
            }

            // ts2 平均每小时的耗油量
            float ty2Consumption = 0f;
            if (ty2FuelConsumptionsDict.TryGetValue(fuelType, out var t2Consumption))
            {
                ty2Consumption = t2Consumption / ty2Hours;
            }

            fuelConsumptionComparisons.Add(new FuelConsumptionComparisonDto(fuelType, ty1Consumption, ty2Consumption));
        }

        return fuelConsumptionComparisons;
    }
    
    private async Task<IReadOnlyList<FuelConsumptionComparisonDto>> YearAverageFuelConsumptionComparisonByLA(
        Guid vesselId, int year1, int year2, VesselEeConfig vesselEeConfig)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        // TODO: 是否允许比较当年的油耗？时间用年的时间还是航行时间？
        var (_, year1StartDate, year1EndDate) = QueryDateTimeUtils.ProcessForPastYearQuery(year1, utcNow);

        var (_, year2StartDate, year2EndDate) = QueryDateTimeUtils.ProcessForPastYearQuery(year2, utcNow);

        // year1 对应的小时数
        float ty1Hours = QueryDateTimeUtils.GetHoursBetween(year1StartDate, year1EndDate);

        // year2 对应的小时数
        float ty2Hours = QueryDateTimeUtils.GetHoursBetween(year2StartDate, year2EndDate);

        if (ty1Hours <= 0 || ty2Hours <= 0)
        {
            return new List<FuelConsumptionComparisonDto>();
        }

        var fuelConsumptionComparisons = new List<FuelConsumptionComparisonDto>();

        var ty1LogAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, year1StartDate, year1EndDate);
        var ty2LogAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, year2StartDate, year2EndDate);

        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            // ts1 平均每小时的耗油量
            float ty1Consumption = ty1LogAbstracts.GetTotalWithEmrgConsumptionByFuelType(fuelType) / ty1Hours;

            // ts2 平均每小时的耗油量
            float ty2Consumption = ty2LogAbstracts.GetTotalWithEmrgConsumptionByFuelType(fuelType) / ty2Hours;

            fuelConsumptionComparisons.Add(new FuelConsumptionComparisonDto(fuelType, ty1Consumption, ty2Consumption));
        }

        return fuelConsumptionComparisons;
    }

    // /// <inheritdoc />
    // public async Task<FuelConsumptionWithTypeDto> DieselGeneratorsInstantFuelConsumptionAsync(Guid vesselId)
    // {
    //     var utcNow = _phmeeTimeProvider.GetUtcNow();
    //
    //     var aeIn = await _flowMeterTdRepository.GetAeInLatestFuelOilValueAsync(vesselId, utcNow);
    //     if (aeIn is null)
    //     {
    //         return new FuelConsumptionWithTypeDto();
    //     }
    //
    //     // 使用 aeIn 的 ts 查询 aeOut
    //     var aeOut = await _flowMeterTdRepository.GetAeOutLatestFuelOilValueAsync(vesselId, aeIn.Ts);
    //     if (aeOut is null)
    //     {
    //         return new FuelConsumptionWithTypeDto();
    //     }
    //
    //     var fuelConsumption = aeIn.OilValue - aeOut.OilValue;
    //
    //     return new FuelConsumptionWithTypeDto
    //     {
    //         FuelConsumption = fuelConsumption,
    //         FuelType = aeIn.FuelType
    //     };
    // }

    /// <inheritdoc />
    public async Task<IEnumerable<FuelMeasurement>> RefuelSingleTimeAsync(Guid vesselId, DateTime? time)
    {
        var selectedTime = time ?? _phmeeTimeProvider.GetUtcNow();
        var startTime = selectedTime.AddDays(-1);

        var bunkerReports = await _bunkerReportRepository.GetListASCAsync(vesselId, startTime, selectedTime);

        // 根据油品统计
        var result = bunkerReports.GroupBy(br => br.FuelType)
            .Select(g => new FuelMeasurement(g.Key, g.Sum(br => br.Mass))).ToList();

        // 如果没有船舶 EE 配置，则直接返回根据油品统计的结果
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return result;
        }

        // 如果配置了船舶用的油品，则每一种油品都要返回，即使没有加油记录
        var resultAllFuels = new List<FuelMeasurement>();
        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            var fuelMeasurement = result.FirstOrDefault(r => r.FuelType == fuelType);
            resultAllFuels.Add(fuelMeasurement ?? new FuelMeasurement(fuelType, 0));
        }

        return resultAllFuels;
    }

    /// <inheritdoc />
    public async Task<IEnumerable<FuelMeasurement>> RefuelSummaryInDateRangeAsync(Guid vesselId, DateTime startDate,
        DateTime endDate)
    {
        // var (startDateTime, endDateTime) = CBMDateTimeHelper.SetDateTimesUtcKind(startDate, endDate);

        var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForDateRangeQuery(startDate, endDate);

        var bunkerReports = await _bunkerReportRepository.GetListASCAsync(vesselId, startDateTime, endDateTime);

        var result = bunkerReports.GroupBy(br => br.FuelType)
            .Select(g => new FuelMeasurement(g.Key, g.Sum(br => br.Mass))).ToList();

        // 如果没有船舶 EE 配置，则直接返回根据油品统计的结果
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return result;
        }

        // 如果配置了船舶用的油品，则每一种油品都要返回，即使没有加油记录
        var resultAllFuels = new List<FuelMeasurement>();
        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            var fuelMeasurement = result.FirstOrDefault(r => r.FuelType == fuelType);
            resultAllFuels.Add(fuelMeasurement ?? new FuelMeasurement(fuelType, 0));
        }

        return resultAllFuels;
    }

    /// <inheritdoc />
    public async Task<IEnumerable<FuelMeasurement>> RefuelSummaryInYearAsync(Guid vesselId, int? year)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var (_, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        var bunkerReports = await _bunkerReportRepository.GetListASCAsync(vesselId, startDateTime, endDateTime);

        var result = bunkerReports.GroupBy(br => br.FuelType)
            .Select(g => new FuelMeasurement(g.Key, g.Sum(br => br.Mass))).ToList();

        // 如果没有船舶 EE 配置，则直接返回根据油品统计的结果
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return result;
        }

        // 如果配置了船舶用的油品，则每一种油品都要返回，即使没有加油记录
        var resultAllFuels = new List<FuelMeasurement>();
        foreach (var fuelType in vesselEeConfig.FuelTypes)
        {
            var fuelMeasurement = result.FirstOrDefault(r => r.FuelType == fuelType);
            resultAllFuels.Add(fuelMeasurement ?? new FuelMeasurement(fuelType, 0));
        }

        return resultAllFuels;
    }

    // /// <inheritdoc />
    // public async Task<FuelFlowMeterValues> FuelFlowMeterValuesAsync(Guid vesselId)
    // {
    //     var now = _phmeeTimeProvider.GetUtcNow();
    //
    //     var meIn = await _flowMeterTdRepository.GetMeInLatestFuelOilValueAsync(vesselId, now);
    //     var meOut = await _flowMeterTdRepository.GetMeOutLatestFuelOilValueAsync(vesselId, now);
    //     var aeIn = await _flowMeterTdRepository.GetAeInLatestFuelOilValueAsync(vesselId, now);
    //     var aeOut = await _flowMeterTdRepository.GetAeOutLatestFuelOilValueAsync(vesselId, now);
    //     var boIn = await _flowMeterTdRepository.GetBoInLatestFuelOilValueAsync(vesselId, now);
    //     var boOut = await _flowMeterTdRepository.GetBoOutLatestFuelOilValueAsync(vesselId, now);
    //
    //     return new FuelFlowMeterValues
    //     {
    //         MainEngineInletFuelFlow = meIn?.OilValue ?? null,
    //         MainEngineOutletFuelFlow = meOut?.OilValue ?? null,
    //         AuxiliaryEngineInletFuelFlow = aeIn?.OilValue ?? null,
    //         AuxiliaryEngineOutletFuelFlow = aeOut?.OilValue ?? null,
    //         BoilerInletFuelFlow = boIn?.OilValue ?? null,
    //         BoilerOutletFuelFlow = boOut?.OilValue ?? null
    //     };
    // }
    
    public async Task<List<FuelConsumptionAnalyzeDto>> FuelConsumptionAnalyzeAsync(Guid vesselId, FuelConsumptionAnalyzePeriod period, FuelType fuelType)
    {
        // 校验 period 和 FuelType 是否合法
        if (!EnumValidationHelper.IsValidEnumValue<FuelConsumptionAnalyzePeriod>((int)period))
        {
            throw new UserFriendlyException("Invalid value for period");
        }
        if (!EnumValidationHelper.IsValidEnumValue<FuelType>((int)fuelType))
        {
            throw new UserFriendlyException("Invalid value for fuelType");
        }
        
        var vesselEeConfig = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return [];
        }
        
        // 判断 FuelType 是否是该船的船用油品
        if (!vesselEeConfig.FuelTypes.Contains(fuelType))
        {
            throw new UserFriendlyException("Invalid value for fuelType");
        }
        
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        
        var flowMeterConfig = await _fuelConsumptionCalculator.GetFlowMeterConfigAsync(vesselId);
        // 是否嵌套安装主机副机
        bool isMeAeNestedInstallation = flowMeterConfig.IsMeAeNestedInstallation();

        // 获取到主机或主副机最新一条数据的 Ts，即为最新的一条数据的时间戳
        string? meOrMeAeInletFlowMeterName = isMeAeNestedInstallation 
            ? flowMeterConfig.GetFlowMeterName(DeviceFuelEnum.MainAndAssist, true) 
            : flowMeterConfig.GetFlowMeterName(DeviceFuelEnum.MainEngine, true);
        if (string.IsNullOrWhiteSpace(meOrMeAeInletFlowMeterName))
        {
            throw new UserFriendlyException(L[PHMEEEnergyEfficiencyErrorCodes.FlowMeterModeNotConfigured],
                PHMEEEnergyEfficiencyErrorCodes.FlowMeterModeNotConfigured);
        }
        var meOrMeAeIn = await _flowMeterTdRepository.GetLatestFuelOilValueAsync(vesselId, utcNow, meOrMeAeInletFlowMeterName);
        if (meOrMeAeIn is null)
        {
            return [];
        }

        var endDateTime = meOrMeAeIn.Ts;

        var startDateTime = period switch
        {
            FuelConsumptionAnalyzePeriod.Live => endDateTime.AddMinutes(-10),
            FuelConsumptionAnalyzePeriod.ThreeMinutes => endDateTime.AddMinutes(-3 * 10),
            FuelConsumptionAnalyzePeriod.FiveMinutes => endDateTime.AddMinutes(-5 * 10),
            FuelConsumptionAnalyzePeriod.FifteenMinutes => endDateTime.AddMinutes(-15 * 10),
            FuelConsumptionAnalyzePeriod.Hourly => endDateTime.AddHours(-1 * 10),
            FuelConsumptionAnalyzePeriod.Daily => endDateTime.AddDays(-1 * 10),
            _ => throw new ArgumentOutOfRangeException(nameof(period), period, null)
        };

        var meOrMeAeInTask = _flowMeterTdRepository.GetListAsync(vesselId, startDateTime, endDateTime, meOrMeAeInletFlowMeterName, TdOrder.Asc, TdRangeEdge.OpenClose);
        string? meOrMeAeReturnFlowMeterName = isMeAeNestedInstallation 
            ? flowMeterConfig.GetFlowMeterName(DeviceFuelEnum.MainAndAssist, false) 
            : flowMeterConfig.GetFlowMeterName(DeviceFuelEnum.MainEngine, false);
        var meOrMeAeOutTask = string.IsNullOrWhiteSpace(meOrMeAeReturnFlowMeterName) ? Task.FromResult(new List<FlowMeterOilValueTde>()) : _flowMeterTdRepository.GetListAsync(vesselId, startDateTime, endDateTime, meOrMeAeReturnFlowMeterName, TdOrder.Asc, TdRangeEdge.OpenClose);
        
        string? aeInletFlowMeterName = flowMeterConfig.GetFlowMeterName(DeviceFuelEnum.AuxiliaryEngine, true);
        var aeInTask = string.IsNullOrWhiteSpace(aeInletFlowMeterName) ? Task.FromResult(new List<FlowMeterOilValueTde>()) : _flowMeterTdRepository.GetListAsync(vesselId, startDateTime, endDateTime, aeInletFlowMeterName, TdOrder.Asc, TdRangeEdge.OpenClose);
        string? aeReturnFlowMeterName = flowMeterConfig.GetFlowMeterName(DeviceFuelEnum.AuxiliaryEngine, false);
        var aeOutTask = string.IsNullOrWhiteSpace(aeReturnFlowMeterName) ? Task.FromResult(new List<FlowMeterOilValueTde>()) : _flowMeterTdRepository.GetListAsync(vesselId, startDateTime, endDateTime, aeReturnFlowMeterName, TdOrder.Asc, TdRangeEdge.OpenClose);
        string? boInletFlowMeterName = flowMeterConfig.GetFlowMeterName(DeviceFuelEnum.Boiler, true);
        var boInTask = string.IsNullOrWhiteSpace(boInletFlowMeterName) ? Task.FromResult(new List<FlowMeterOilValueTde>()) : _flowMeterTdRepository.GetListAsync(vesselId, startDateTime, endDateTime, boInletFlowMeterName, TdOrder.Asc, TdRangeEdge.OpenClose);
        string? boReturnFlowMeterName = flowMeterConfig.GetFlowMeterName(DeviceFuelEnum.Boiler, false);
        var boOutTask = string.IsNullOrWhiteSpace(boReturnFlowMeterName) ? Task.FromResult(new List<FlowMeterOilValueTde>()) : _flowMeterTdRepository.GetListAsync(vesselId, startDateTime, endDateTime, boReturnFlowMeterName, TdOrder.Asc, TdRangeEdge.OpenClose);
        
        await Task.WhenAll(meOrMeAeInTask, meOrMeAeOutTask, aeInTask, aeOutTask, boInTask, boOutTask);

        var meOrMeAeInList = await meOrMeAeInTask;
        var meOrMeAeOutList = await meOrMeAeOutTask;
        var aeInList = await aeInTask;
        var aeOutList = await aeOutTask;
        var boInList = await boInTask;
        var boOutList = await boOutTask;

        // 根据 Period 划分时间段，然后计算每个时间段的油耗。例如 Period 为 ThreeMinutes，则每 3 分钟计算一次油耗，要从 endDateTime 开始往前推 10 个时间段，也就是 10 个 3 分钟
        List<KeyValuePair<DateTime, DateTime>> timeRanges = new List<KeyValuePair<DateTime, DateTime>>();
        switch (period)
        {
            case FuelConsumptionAnalyzePeriod.Live:
                // Live 时，要往前推 10 个 1 分钟
                for (int i = 0; i < 10; i++)
                {
                    var end = endDateTime.AddMinutes(-1 * i);
                    var start = endDateTime.AddMinutes(-1 * (i + 1));
                    timeRanges.Add(new KeyValuePair<DateTime, DateTime>(start, end));
                }
                break;
            case FuelConsumptionAnalyzePeriod.ThreeMinutes:
                for (int i = 0; i < 10; i++)
                {
                    var end = endDateTime.AddMinutes(-3 * i);
                    var start = endDateTime.AddMinutes(-3 * (i + 1));
                    timeRanges.Add(new KeyValuePair<DateTime, DateTime>(start, end));
                }
                break;
            case FuelConsumptionAnalyzePeriod.FiveMinutes:
                for (int i = 0; i < 10; i++)
                {
                    var end = endDateTime.AddMinutes(-5 * i);
                    var start = endDateTime.AddMinutes(-5 * (i + 1));
                    timeRanges.Add(new KeyValuePair<DateTime, DateTime>(start, end));
                }
                break;
            case FuelConsumptionAnalyzePeriod.FifteenMinutes:
                for (int i = 0; i < 10; i++)
                {
                    var end = endDateTime.AddMinutes(-15 * i);
                    var start = endDateTime.AddMinutes(-15 * (i + 1));
                    timeRanges.Add(new KeyValuePair<DateTime, DateTime>(start, end));
                }
                break;
            case FuelConsumptionAnalyzePeriod.Hourly:
                for (int i = 0; i < 10; i++)
                {
                    var end = endDateTime.AddHours(-1 * i);
                    var start = endDateTime.AddHours(-1 * (i + 1));
                    timeRanges.Add(new KeyValuePair<DateTime, DateTime>(start, end));
                }
                break;
            case FuelConsumptionAnalyzePeriod.Daily:
                for (int i = 0; i < 10; i++)
                {
                    var end = endDateTime.AddDays(-1 * i);
                    var start = endDateTime.AddDays(-1 * (i + 1));
                    timeRanges.Add(new KeyValuePair<DateTime, DateTime>(start, end));
                }
                break;
            default:
                throw new UserFriendlyException("Invalid value for period");
        }

        // 根据时间 ASC 排序
        timeRanges = timeRanges.OrderBy(tr => tr.Key).ToList();
        
        var fuelConsumptionAnalyzeDtos = new List<FuelConsumptionAnalyzeDto>(10);

        foreach (var (start, end) in timeRanges)
        {
            // 主机 or 主副机
            var meOrMeAeInRecords = meOrMeAeInList.Where(x => x.Ts > start && x.Ts <= end).ToList();
            var meOrMeAeOutRecords = meOrMeAeOutList.Where(x => x.Ts > start && x.Ts <= end).ToList();
            var meOrMeAeDict = FuelConsumptionCalculator.CalculateFuelConsumption(meOrMeAeInRecords, meOrMeAeOutRecords, start, end);
            
            // 副机
            var aeInRecords = aeInList.Where(x => x.Ts > start && x.Ts <= end).ToList();
            var aeOutRecords = aeOutList.Where(x => x.Ts > start && x.Ts <= end).ToList();
            var aeDict = FuelConsumptionCalculator.CalculateFuelConsumption(aeInRecords, aeOutRecords, start, end);
            float aeConsumption = aeDict.TryGetValue(fuelType, out var aeValue) ? aeValue : 0;

            Dictionary<FuelType, float> meDict = meOrMeAeDict;
            // 如果是主机副机嵌套安装，则主机的需要减出来
            if (isMeAeNestedInstallation)
            {
                meDict = FuelConsumptionCalculator.FuelConsumptionDictionaryMinus(meOrMeAeDict, aeDict);
            }
            float meConsumption = meDict.TryGetValue(fuelType, out var meValue) ? meValue : 0;
            
            // 锅炉
            var boInRecords = boInList.Where(x => x.Ts > start && x.Ts <= end).ToList();
            var boOutRecords = boOutList.Where(x => x.Ts > start && x.Ts <= end).ToList();
            var boDict = FuelConsumptionCalculator.CalculateFuelConsumption(boInRecords, boOutRecords, start, end);
            float boConsumption = boDict.TryGetValue(fuelType, out var boValue) ? boValue : 0;
            
            // 总
            float totalConsumption = meConsumption + aeConsumption + boConsumption;
            
            fuelConsumptionAnalyzeDtos.Add(new FuelConsumptionAnalyzeDto
            {
                // 将时间段的结束时间作为本次统计分析的时间
                // 单位由 t 转为 kg
                Ts = end,
                MeValue = meConsumption * 1000f,
                AeValue = aeConsumption * 1000f,
                BoValue = boConsumption * 1000f,
                TotalValue = totalConsumption * 1000f
            });
        }

        return fuelConsumptionAnalyzeDtos;
    }
    
    public async Task<bool> CheckLAFuelPasswordAsync(Guid vesselId, string laFuelPassword)
    {
        var vesselEeConfig = await _vesselEeConfigRepository.GetByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return false;
        }

        return vesselEeConfig.LogAbstractPass == laFuelPassword;
    }

    public async Task<bool> ChangeLAFuelPasswordAsync(Guid vesselId, string newLaFuelPassword)
    {
        var vesselEeConfig = await _vesselEeConfigRepository.GetByVesselIdAsync(vesselId);
        if (vesselEeConfig is null)
        {
            return false;
        }

        vesselEeConfig.LogAbstractPass = newLaFuelPassword;
        await _vesselEeConfigRepository.UpdateAsync(vesselEeConfig);

        return true;
    }
}